// Header Protection
#ifndef _TEXTUREMANAGER_H_
#define _TEXTUREMANAGER_H_

// Include Files
#include "Event\Event.h"

// Forward Declarations
class CTextureEventArg;

/////////////////////////////////////////////////////////////////////////////////////
// This enumeration contains all constant values used by textures
enum texture_e
{
	// texture types
	TEXTURE_TYPE_2D           = 0x0DE1, // GL_TEXTURE_2D
	TEXTURE_TYPE_CUBE         = 0x8513, // GL_TEXTURE_CUBE_MAP

	// texture targets
	TEXTURE_TARGET_2D         = 0x0DE1, // GL_TEXTURE_2D
	TEXTURE_TARGET_CUBE_POS_X = 0x8515, // GL_TEXTURE_CUBE_MAP_POSITIVE_X
	TEXTURE_TARGET_CUBE_NEG_X = 0x8516, // GL_TEXTURE_CUBE_MAP_NEGATIVE_X
	TEXTURE_TARGET_CUBE_POS_Y = 0x8517, // GL_TEXTURE_CUBE_MAP_POSITIVE_Y
	TEXTURE_TARGET_CUBE_NEG_Y = 0x8518, // GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
	TEXTURE_TARGET_CUBE_POS_Z = 0x8519, // GL_TEXTURE_CUBE_MAP_POSITIVE_Z
	TEXTURE_TARGET_CUBE_NEG_Z = 0x851A, // GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
};

/////////////////////////////////////////////////////////////////////////////////////
// This class maintains a list of all loaded textures and provides an indexing 
//     system in order to allow access to the textures on an instanced basis.
class CTextureManager : public ISingleton<CTextureManager>
{
public:     // methods

	// Initialize texture management.
	void Initialize();

	// Shut down texture management.
	void Shutdown();

	// Load a texture file, send a TextureLoad event with the file data.
	// _strFilename : string_t - the name of the file to load
	void LoadTextureFile( string_t _strFilename );

	// Create an OpenGL texture from the TextureLoad event argument.
	// _rArg : CTextureLoadEventArg const & - the event 
	//     argument to create a texture from
	void CreateTexture( CTextureEventArg const &_rArg );

	// Destroys the OpenGL texture with the given name.
	// _strName : string_t - the name of the texture to destroy.
	void DestroyTexture( string_t _strName );

	// Get the OpenGL texture with the given name.
	// _strName : string_t - the name of the texture to get
	// return : uint_t - the OpenGL texture index
	uint_t GetTexture( string_t _strName );

public:     // events

	// A texture has been loaded from a file and needs to be created.
	CEvent<CTextureEventArg> TextureLoad;

private:    // friend methods
	friend class ISingleton<CTextureManager>;

	CTextureManager() : m_lstTextures() { }
	~CTextureManager() { }

private:    // structures
	struct TTexture
	{
		string_t strName;
		uint_t   uTexture;
	};

private:    // data
	list_t<TTexture> m_lstTextures;

private:    // dummy definitions
	CTextureManager( CTextureManager const & );
	CTextureManager &operator =( CTextureManager const & );
};

/////////////////////////////////////////////////////////////////////////////////////
// This class is an event argument used to pass texture data loaded from a file to 
//     the renderer where it can be loaded into the rendering system.
class CTextureEventArg : public CEventArg
{
public:     // accessors

	// texture data
	inline string_t const &GetName() const
	{ return m_strName; }

	inline texture_e GetType() const
	{ return m_eType; }

	inline uint_t GetImageCount() const
	{ return m_lstpImages.GetSize(); }

	inline uint_t GetMipCount() const 
	{ return m_uMipCount; }

	inline uint_t GetWidth() const 
	{ return m_uWidth; }

	inline uint_t GetHeight() const 
	{ return m_uHeight; }

	// active image data, acts on the current active image
	inline texture_e GetImageTarget() const
	{ return m_pActiveImage->eTarget; }

	inline uint_t GetImageMipCount() const
	{ return m_pActiveImage->lstpMips.GetSize(); }

	inline byte_t const *GetImageMip( uint_t _uMip ) const
	{ return m_pActiveImage->lstpMips[_uMip]; }

public:     // methods

	// _strName : string_t - the name to use to reference this texture
	// _eType : texture_e - the type of this texture
	inline CTextureEventArg( string_t _strName, texture_e _eType, 
		uint_t _uImageCount, uint_t _uMipCount, uint_t _uWidth, uint_t _uHeight ) : 
	CEventArg(), m_strName(_strName), m_eType(_eType), m_lstpImages(_uImageCount), 
	m_uMipCount(_uMipCount), m_uWidth(_uWidth), m_uHeight(_uHeight)
	{ }

	inline ~CTextureEventArg()
	{
		uint_t uImageCount = m_lstpImages.GetSize();
		for( uint_t uImage = 0; uImage < uImageCount; ++uImage )
		{
			delete m_lstpImages[uImage];
		}
	}

	// Add an image to the texture and set it as the active image.
	// _eTarget : texture_e - the target where the image should be loaded
	inline void AddImage( texture_e _eTarget )
	{
		m_pActiveImage = new TImage(_eTarget, m_uMipCount);
		m_lstpImages.Append(m_pActiveImage);
	}

	// Add a mip level to the active image.
	// _pData : byte_t const * - the data for the new mip.
	inline void AddImageMip( byte_t const *_pData )
	{ m_pActiveImage->lstpMips.Append(_pData); }

	// Set the active image.
	// _uImage : uint_t - the image to set as active
	inline void ActiveImage( uint_t _uImage ) const
	{ m_pActiveImage = m_lstpImages[_uImage]; }

private:    // structures
	struct TImage
	{
	public:    // data
		texture_e               eTarget;
		flist_t<byte_t const *> lstpMips;

	public:     // methods

		// _eTarget : texture_e - the target where the image should be loaded
		// _uMipCount : uint_t - the number of mips the image will have
		inline TImage( texture_e _eTarget, uint_t _uMipCount ) :
		eTarget(_eTarget), lstpMips(_uMipCount)
		{ }

		inline ~TImage()
		{
			uint_t uMipCount = lstpMips.GetSize();
			for( uint_t uMip = 0; uMip < uMipCount; ++uMip )
			{
				delete[] lstpMips[uMip];
			}
		}

	private:     // dummy definitions
		TImage( TImage const & );
		TImage &operator =( TImage const & );
	};

private:    // data
	string_t          m_strName;
	texture_e         m_eType;
	uint_t            m_uWidth;
	uint_t            m_uHeight;
	uint_t            m_uMipCount;
	flist_t<TImage *> m_lstpImages;
	mutable TImage *  m_pActiveImage;

private:    // dummy definitions
	CTextureEventArg( CTextureEventArg const & );
	CTextureEventArg &operator =( CTextureEventArg const & );
};

/////////////////////////////////////////////////////////////////////////////////////
// Header Protection
#endif // _TEXTUREMANAGER_H_
